/*
 * Copyright (c) 2018 - 2028. www.vtuzx.com Inc. All Rights Reserved
 */
package com.vtuzx.kingdee.xqc_api.service.impl;

import com.vtuzx.core.VtuzxException;
import com.vtuzx.core.bean.dto.VtuzxMap;
import com.vtuzx.core.bean.dto.VtuzxPage;
import com.vtuzx.core.bean.dto.VtuzxToken;
import com.vtuzx.core.constant.IVtuzxConst;
import com.vtuzx.core.file.excel.VtuzxExcel;
import com.vtuzx.core.file.excel.VtuzxExcelCell;
import com.vtuzx.core.file.excel.VtuzxExcelRow;
import com.vtuzx.core.file.excel.VtuzxExcelSheet;
import com.vtuzx.core.utils.VtuzxUtil;
import com.vtuzx.fast.utils.FastUtil;
import com.vtuzx.kingdee.xqc_api.service.IXqcBasicService;
import com.vtuzx.kingdee.xqc_api.service.IXqcInventoryService;
import com.vtuzx.kingdee.xqc_api.service.IXqcStockTakeService;
import com.vtuzx.kingdee.xqc_core.bean.VtuzxArrayList;
import com.vtuzx.kingdee.xqc_core.bean.xqc.BillMaterial;
import com.vtuzx.kingdee.xqc_core.bean.xqc.BillMaterialBox;
import com.vtuzx.kingdee.xqc_core.constant.BillEvent;
import com.vtuzx.kingdee.xqc_core.constant.BillStatus;
import com.vtuzx.kingdee.xqc_core.constant.BillType;
import com.vtuzx.kingdee.xqc_core.constant.IXqcConst;
import com.vtuzx.kingdee.xqc_core.dao.*;
import com.vtuzx.web.bean.VtuzxDownloadBean;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.print.Doc;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

@Service
public class XqcStockTakeServiceImpl implements IXqcStockTakeService {

    /** Logger */
    private static final Logger _logger = LoggerFactory.getLogger(XqcStockTakeServiceImpl.class);

    @Autowired
    StockTakeDao stockTakeDao;

    @Autowired
    StockTakeTaskDao stockTakeTaskDao;

    @Autowired
    StockTakeTaskDetailDao stockTakeTaskDetailDao;

    @Autowired
    StockTakeTaskDetailBoxDao stockTakeTaskDetailBoxDao;

    @Autowired
    ProfitAndLossDao profitAndLossDao;

    @Autowired
    ProfitAndLossDetailDao profitAndLossDetailDao;

    @Autowired
    ProfitAndLossDetailBoxDao profitAndLossDetailBoxDao;

    @Autowired
    InventoryDao inventoryDao;

    @Autowired
    InventoryDetailDao inventoryDetailDao;

    @Autowired
    MaterialDao materialDao;

    @Autowired
    StockDao stockDao;

    @Autowired
    StockDetailDao stockDetailDao;

    @Autowired
    IXqcBasicService xqcBasicService;

    @Autowired
    private IXqcInventoryService inventoryService;



    @Override
    public VtuzxMap saveStockTake(ObjectId userId, String name, Map<String, Object> param) {
        String billNo = IXqcConst.createBillNo(BillType.PDFA);
        String billName = VtuzxUtil.getString(param, "billName");
        String stockFrom = VtuzxUtil.getString(param, "stockFrom");
        String stockTo = VtuzxUtil.getString(param, "stockTo");
        String materialFrom = VtuzxUtil.getString(param, "materialFrom");
        String materialTo = VtuzxUtil.getString(param, "materialTo");
        boolean addMaterial = VtuzxUtil.getBoolean(param, "addMaterial");
        boolean stockTakeMethod = VtuzxUtil.getBoolean(param, "stockTakeMethod");
        boolean review = VtuzxUtil.getBoolean(param, "review");
        Document stockTakeDoc = new Document();
        stockTakeDoc.append("billNo", billNo);
        stockTakeDoc.append("_createUserId", userId);
        stockTakeDoc.append("createUserName", name);
        stockTakeDoc.append("billName", billName);
        stockTakeDoc.append("stockFrom", stockFrom);
        stockTakeDoc.append("stockTo", stockTo);
        stockTakeDoc.append("materialFrom", materialFrom);
        stockTakeDoc.append("materialTo", materialTo);
        stockTakeDoc.append("addMaterial", addMaterial);
        stockTakeDoc.append("stockTakeMethod", stockTakeMethod);
        stockTakeDoc.append("review", review);
        stockTakeDoc.append("status", BillStatus.Save.toString());
        stockTakeDao.insertBill(userId, name, stockTakeDoc);

        return new VtuzxMap("billNo", billNo);
    }

    @Override
    public void auditStockTake(ObjectId userId, String name, Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");
        stockTakeDao.audit(userId, name, billNo);
    }

    @Override
    public void unauditStockTake(ObjectId userId, String name, Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");
        stockTakeDao.unaudit(billNo);

    }

    @Override
    public VtuzxMap getStockTakeDetail(ObjectId userId, String billNo) throws VtuzxException {
        Document doc = stockTakeDao.findOne(Criteria.where("billNo").is(billNo));
        if (doc == null) {
            throw new VtuzxException(IVtuzxConst.Msg_E_target_not_exist);
        }
        VtuzxMap header = new VtuzxMap(doc);
        return new VtuzxMap("header", header);
    }

    @Override
    public void deleteStockTake(ObjectId userId, String name, Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");
        stockTakeDao.deleteOne(Criteria.where("billNo").is(billNo));
    }

    @Override
    public VtuzxMap getStockTakeList(ObjectId userId, String name, Map<String, Object> param) {
        int pageSize = VtuzxUtil.getInt(param, "pageSize");
        int pageIndex = VtuzxUtil.getInt(param, "pageIndex");
        Criteria cond = new Criteria();
        VtuzxPage page = stockTakeDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        List<VtuzxMap> dataList = page.iterator(doc -> new VtuzxMap("id", VtuzxUtil.getString(doc, IVtuzxConst.Key__id))
                .append("billNo", VtuzxUtil.getString(doc, "billNo"))
                .append("billName", VtuzxUtil.getString(doc, "billName"))
                .append("stockFrom", VtuzxUtil.getString(doc, "stockFrom"))
                .append("stockTo", VtuzxUtil.getString(doc, "stockTo"))
                .append("materialFrom", VtuzxUtil.getString(doc, "materialFrom"))
                .append("materialTo", VtuzxUtil.getString(doc, "materialTo"))
                .append("addMaterial", VtuzxUtil.getBoolean(doc, "addMaterial"))
                .append("stockTakeMethod", VtuzxUtil.getBoolean(doc, "stockTakeMethod"))
                .append("review", VtuzxUtil.getBoolean(doc, "review"))
                .append("status", VtuzxUtil.getString(doc, "status"))
                .append("createTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(doc, "createTime")))
        );
        return new VtuzxMap(IVtuzxConst.Key_total, page.total()).append(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public void toStockTakeTask(ObjectId userId, String name, Map<String, Object> param) throws VtuzxException {
        String stockTakeNo = VtuzxUtil.getString(param, "billNo");
        Document stockTakeDoc = stockTakeDao.findOne(Criteria.where("billNo").is(stockTakeNo));
        String billName = VtuzxUtil.getString(stockTakeDoc, "billName");
        String stockFrom = VtuzxUtil.getString(stockTakeDoc, "stockFrom");
        String stockTo = VtuzxUtil.getString(stockTakeDoc, "stockTo");
        String materialFrom = VtuzxUtil.getString(stockTakeDoc, "materialFrom");
        String materialTo = VtuzxUtil.getString(stockTakeDoc, "materialTo");
        boolean addMaterial = VtuzxUtil.getBoolean(stockTakeDoc, "addMaterial");
        boolean stockTakeMethod = VtuzxUtil.getBoolean(stockTakeDoc, "stockTakeMethod");
        boolean review = VtuzxUtil.getBoolean(stockTakeDoc, "review");
        String billNo = IXqcConst.createBillNo(BillType.PDZY);

        Document taskDoc = stockTakeTaskDao.findOne(Criteria.where("stockTakeNo").is(stockTakeNo));
        if (!VtuzxUtil.isEmpty(taskDoc)) {
            throw new VtuzxException("单据不能重复下发");
        }
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(materialFrom) || !VtuzxUtil.isEmpty(materialTo)) {
            cond = cond.and("materialNo");
            if (!VtuzxUtil.isEmpty(materialFrom)) {
                cond.gte(materialFrom);
            }
            if (!VtuzxUtil.isEmpty(materialTo)) {
                cond.lte(materialTo);
            }
        }
        if (!VtuzxUtil.isEmpty(stockFrom) || !VtuzxUtil.isEmpty(stockTo)) {
            cond = cond.and("stockNo");
            if (!VtuzxUtil.isEmpty(stockFrom)) {
                cond.gte(stockFrom);
            }
            if (!VtuzxUtil.isEmpty(stockTo)) {
                cond.lte(stockTo);
            }
        }
//        cond.and("materialNo").gte(materialFrom).lte(materialTo).and("stockNo").gte(stockFrom).lte(stockTo);

//        Criteria c = new Criteria("stockNo").gte(stockFrom).lte(stockTo);
        Iterator<Document> inventoryItr = inventoryDao.findList(cond, new Document("createTime", 1), 0, 0);
        List<ObjectId> _inventoryIdList = new ArrayList<>();
        List<String> materialNoList = new ArrayList<>();
        VtuzxMap inventoryMap = new VtuzxMap();
        while (inventoryItr.hasNext()) {
            Document inventoryDoc = inventoryItr.next();
            ObjectId _id = VtuzxUtil.getObject(inventoryDoc, IVtuzxConst.Key__id);
            String materialNo = VtuzxUtil.getString(inventoryDoc, "materialNo");
            _inventoryIdList.add(_id);
            materialNoList.add(materialNo);
            inventoryMap.append(_id.toString(), inventoryDoc);
        }
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materialNoList), null, 0, 0);
        VtuzxMap materialMap = FastUtil.itr2Map(materialItr, "fNumber");

        //不带沫沫的
        Iterator<Document> inventoryDetailItr = inventoryDetailDao.findList(Criteria.where("_inventoryId").in(_inventoryIdList).and("status").is("in").and("materialNo").ne("03010101042"), null, 0, 0);

        Map<String, Document> stockTakeDetailMap = new HashMap<>();
        List<Document> stockTakeDetailBoxList = new ArrayList<>();
        while (inventoryDetailItr.hasNext()) {
            Document inventoryDetailDoc = inventoryDetailItr.next();
            String boxNo = VtuzxUtil.getString(inventoryDetailDoc, "boxNo");
            String locationNo = VtuzxUtil.getString(inventoryDetailDoc, "locationNo");
            double count = VtuzxUtil.getDouble(inventoryDetailDoc, "count");
            String inventoryId = VtuzxUtil.getString(inventoryDetailDoc, "_inventoryId");

            // 盘点作业分录
            String key = inventoryId + "_" + locationNo;
            Document detailDoc = stockTakeDetailMap.get(key);
            if (detailDoc == null) {
                Document inventoryDoc = VtuzxUtil.getObject(inventoryMap, inventoryId);
                if (inventoryDoc == null) {
                    continue;
                }
                String materialNo = VtuzxUtil.getString(inventoryDoc, "materialNo");
                Document materialDoc = VtuzxUtil.getObject(materialMap, materialNo);
                if (materialDoc == null) {
                    continue;
                }
                detailDoc = new Document("billNo", billNo);
                detailDoc.append("batchNo", VtuzxUtil.getString(inventoryDoc, "batchNo"));
                detailDoc.append("exp", VtuzxUtil.getInt(inventoryDoc, "exp"));
                detailDoc.append("produceDate",  VtuzxUtil.getString(inventoryDoc, "produceDate"));
                detailDoc.append("materialNo", materialNo);
                detailDoc.append("materialName", VtuzxUtil.getString(materialDoc, "fName"));
                detailDoc.append("materialSpec", VtuzxUtil.getString(materialDoc, "fSpecification"));
                detailDoc.append("unit", VtuzxUtil.getString(materialDoc, "fBaseUnitIdNumber"));
                detailDoc.append("unitName", VtuzxUtil.getString(materialDoc, "fBaseUnitIdFName"));

                detailDoc.append("stockNo", VtuzxUtil.getString(inventoryDoc, "stockNo"));
                detailDoc.append("locationNo", locationNo);
                detailDoc.append("key", key);

                stockTakeDetailMap.put(key, detailDoc);
            }
            int boxCount = VtuzxUtil.getInt(detailDoc, "bookBoxCount");
            double bookCount = VtuzxUtil.getDouble(detailDoc, "bookCount");
            detailDoc.append("bookBoxCount", boxCount + 1);
            detailDoc.append("bookCount", bookCount + count);

            // 盘点作业分录箱
            Document detailBoxDoc = new Document("billNo", billNo);
            detailBoxDoc.append("boxNo", boxNo);
            detailBoxDoc.append("bookCount", count);
            detailBoxDoc.append("key", key);
            detailBoxDoc.append("rfid", VtuzxUtil.getObject(inventoryDetailDoc, "rfid"));
            detailBoxDoc.append("batchNo", VtuzxUtil.getObject(detailDoc, "batchNo"));
            detailBoxDoc.append("exp", VtuzxUtil.getObject(detailDoc, "exp"));
            detailBoxDoc.append("produceDate", VtuzxUtil.getObject(detailDoc, "produceDate"));
            detailBoxDoc.append("materialNo", VtuzxUtil.getObject(detailDoc, "materialNo"));
            detailBoxDoc.append("materialName", VtuzxUtil.getObject(detailDoc, "materialName"));
            detailBoxDoc.append("materialSpec", VtuzxUtil.getObject(detailDoc, "materialSpec"));
            detailBoxDoc.append("unit", VtuzxUtil.getObject(detailDoc, "unit"));
            detailBoxDoc.append("unitName", VtuzxUtil.getObject(detailDoc, "unitName"));
            detailBoxDoc.append("stockNo", VtuzxUtil.getObject(detailDoc, "stockNo"));
            detailBoxDoc.append("locationNo", VtuzxUtil.getObject(detailDoc, "locationNo"));
            stockTakeDetailBoxList.add(detailBoxDoc);
        }

        int sumBoxCount = stockTakeDetailBoxList.size();

        Document doc = new Document("billNo", billNo)
                .append("stockTakeNo", stockTakeNo)
                .append("stockTakeName", billName)
                .append("stockFrom", stockFrom)
                .append("stockTo", stockTo)
                .append("materialFrom", materialFrom)
                .append("materialTo", materialTo)
                .append("addMaterial", addMaterial)
                .append("stockTakeMethod", stockTakeMethod)
                .append("review", review)
                .append("status", BillStatus.Save.toString());
        if (!stockTakeMethod) {
            doc.append("sumBoxCount", sumBoxCount);
        }
        ObjectId _taskId = null;
        try {
            _taskId = stockTakeTaskDao.insertBill(userId, name, doc);
            stockTakeTaskDetailDao.insertMany(StockTakeTaskDetailDao.NAME + billNo, new ArrayList<>(stockTakeDetailMap.values()));
            stockTakeTaskDetailBoxDao.insertMany(StockTakeTaskDetailBoxDao.NAME + billNo, stockTakeDetailBoxList);
        } catch(Exception e) {
            if (_taskId != null) {
                stockTakeTaskDao.deleteById(_taskId);
                stockTakeTaskDetailDao.dropColl(StockTakeTaskDetailDao.NAME + billNo);
                stockTakeTaskDetailBoxDao.dropColl(StockTakeTaskDetailBoxDao.NAME + billNo);
            }
        }
    }

    @Override
    public void auditStockTakeTask(ObjectId userId, String name, Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");
        stockTakeTaskDao.audit(userId, name, billNo);
        stockTakeTaskDetailDao.auditBill(billNo, "save");
        stockTakeTaskDetailBoxDao.auditBill(billNo, "save", "yes", userId, name);
    }

    @Override
    public void unauditStockTakeTask(ObjectId userId, String name, Map<String, Object> param) throws VtuzxException {
        String billNo = VtuzxUtil.getString(param, "billNo");
        Document doc = profitAndLossDao.findOne(Criteria.where("taskBillNo").is(billNo));
        if (!VtuzxUtil.isEmpty(doc)) {
            throw new VtuzxException("已经生成盘盈亏单，不能反审");
        }
        stockTakeTaskDao.unaudit(billNo);
        stockTakeTaskDetailDao.unauditBill(billNo);
        stockTakeTaskDetailBoxDao.unauditBill(billNo);
    }

    @Override
    public VtuzxMap getStockTakeTaskList(ObjectId userId, Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");
        String stockTakeNo = VtuzxUtil.getString(param, "stockTakeNo");
        String stockTakeName = VtuzxUtil.getString(param, "stockTakeName");
        int pageIndex = VtuzxUtil.getInt(param, IVtuzxConst.Key_pageIndex);
        int pageSize = VtuzxUtil.getInt(param, IVtuzxConst.Key_pageSize);
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(billNo)) {
            cond.and("billNo").regex(billNo);
        }
        if (!VtuzxUtil.isEmpty(stockTakeNo)) {
            cond.and("stockTakeNo").regex(stockTakeNo);
        }
        if (!VtuzxUtil.isEmpty(stockTakeName)) {
            cond.and("stockTakeName").regex(stockTakeName);
        }
        VtuzxPage page = stockTakeTaskDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        List<VtuzxMap> dataList = page.iterator(doc -> new VtuzxMap("billNo", VtuzxUtil.getString(doc, "billNo"))
                .append("stockTakeNo", VtuzxUtil.getString(doc, "stockTakeNo"))
                .append("stockTakeName", VtuzxUtil.getString(doc, "stockTakeName"))
                .append("stockFrom", VtuzxUtil.getString(doc, "stockFrom"))
                .append("stockTo", VtuzxUtil.getString(doc, "stockTo"))
                .append("materialFrom", VtuzxUtil.getString(doc, "materialFrom"))
                .append("materialTo", VtuzxUtil.getString(doc, "materialTo"))
                .append("status", VtuzxUtil.getString(doc, "status"))
                .append("addMaterial", VtuzxUtil.getBoolean(doc, "addMaterial"))
                .append("stockTakeMethod", VtuzxUtil.getBoolean(doc, "stockTakeMethod"))
                .append("review", VtuzxUtil.getBoolean(doc, "review"))
                .append("sumBoxCount", VtuzxUtil.getInt(doc, "sumBoxCount"))
                .append("reviewSweptCount", VtuzxUtil.getInt(doc, "reviewSweptCount"))
                .append("sweptCount", VtuzxUtil.getInt(doc, "sweptCount"))
                .append("createTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(doc, "createTime")))
        );
        return new VtuzxMap(IVtuzxConst.Key_total, page.total()).append(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public VtuzxMap getStockTakeTaskDetail(ObjectId userId, String name, Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");
        Document taskDoc = stockTakeTaskDao.findOne(Criteria.where("billNo").is(billNo));
        String materialFrom = VtuzxUtil.getString(taskDoc, "materialFrom");
        Document materialFromDoc = materialDao.findOne(Criteria.where("fNumber").is(materialFrom));
        String materialFromName = "";
        if (!VtuzxUtil.isEmpty(materialFromDoc)) {
            materialFromName = VtuzxUtil.getString(materialFromDoc, "fName");
        }
        String materialTo = VtuzxUtil.getString(taskDoc, "materialTo");
        Document materialToDoc = materialDao.findOne(Criteria.where("fNumber").is(materialTo));
        String materialToName = "";
        if (!VtuzxUtil.isEmpty(materialFromDoc)) {
            materialToName = VtuzxUtil.getString(materialToDoc, "fName");
        }
        VtuzxMap header = new VtuzxMap("billNo", billNo)
                .append("stockTakeNo", VtuzxUtil.getString(taskDoc, "stockTakeNo"))
                .append("stockTakeName", VtuzxUtil.getString(taskDoc, "stockTakeName"))
                .append("stockFrom", VtuzxUtil.getString(taskDoc, "stockFrom"))
                .append("stockTo", VtuzxUtil.getString(taskDoc, "stockTo"))
                .append("status", VtuzxUtil.getString(taskDoc, "status"))
                .append("materialFrom", materialFromName)
                .append("materialTo", materialToName)
                .append("addMaterial", VtuzxUtil.getBoolean(taskDoc, "addMaterial"))
                .append("sumBoxCount", VtuzxUtil.getInt(taskDoc, "sumBoxCount"))
                .append("sweptCount", VtuzxUtil.getInt(taskDoc, "sweptCount"))
                .append("stockTakeMethod", VtuzxUtil.getBoolean(taskDoc, "stockTakeMethod"))
                .append("review", VtuzxUtil.getBoolean(taskDoc, "review"))
                .append("createTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(taskDoc, "createTime")));

        List<VtuzxMap> dataList = new ArrayList<>();
        Iterator<Document> taskDetailItr = stockTakeTaskDetailDao.findList(StockTakeTaskDetailDao.NAME + billNo, null, new Document("status", -1).append("TPALCount", 1).append("createTime", -1), 0, 0);

        while (taskDetailItr.hasNext()) {
            Document taskDetailDoc = taskDetailItr.next();
            String materialNo = VtuzxUtil.getString(taskDetailDoc,"materialNo");
            if (!"03010101042".equals(materialNo)){//不要沫沫的
                VtuzxMap taskDetails = new VtuzxMap(taskDetailDoc);
                taskDetails.remove(IVtuzxConst.Key_updateTime);
                taskDetails.remove(IVtuzxConst.Key_createTime);
                String id = VtuzxUtil.removeString(taskDetails, IVtuzxConst.Key__id);
                taskDetails.append("id", id);
                dataList.add(taskDetails);
            }
        }

        return new VtuzxMap("header", header).append(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public VtuzxMap getStockTakeTaskDetailBox(ObjectId userId, String name, Map<String, Object> param) {
        String key = VtuzxUtil.getString(param, "key");
        String taskBillNo = VtuzxUtil.getString(param, "billNo");
        List<VtuzxMap> dataList = new ArrayList<>();
        Iterator<Document> boxItr = stockTakeTaskDetailBoxDao.findList(StockTakeTaskDetailBoxDao.NAME + taskBillNo, Criteria.where("key").is(key), new Document("boxNo", 1), 0, 0);
        while (boxItr.hasNext()) {
            Document boxDoc = boxItr.next();
            VtuzxMap boxs = new VtuzxMap(boxDoc);
            boxs.remove(IVtuzxConst.Key_updateTime);
            boxs.remove(IVtuzxConst.Key_createTime);
            String boxId = VtuzxUtil.removeString(boxs, IVtuzxConst.Key__id);
            boxs.append("id", boxId);
            dataList.add(boxs);
        }
        return new VtuzxMap(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public VtuzxMap getUserTask(ObjectId userId, String name, Map<String, Object> param) {
        String stockNo = VtuzxUtil.getString(param, "stockNo");
        List<VtuzxMap> dataList = new ArrayList<>();
        Criteria cond = new Criteria();
        cond.orOperator(Criteria.where("stockFrom").lte(stockNo).and("stockTo").gte(stockNo), Criteria.where("stockFrom").is("").and("stockTo").is("")).and("status").is("save");
        Iterator<Document> stockTakeTaskItr = stockTakeTaskDao.findList(cond, new Document("createTime", -1), 0, 0);
        while (stockTakeTaskItr.hasNext()) {
            Document stockTakeTaskDoc = stockTakeTaskItr.next();
            VtuzxMap taskMap = new VtuzxMap(stockTakeTaskDoc);
            String id = VtuzxUtil.removeString(taskMap, IVtuzxConst.Key__id);
            String billNo = VtuzxUtil.getString(taskMap, "billNo");
            boolean review = VtuzxUtil.getBoolean(taskMap, "review");
            taskMap.append("id", id);
            taskMap.append("inventoryType", "first");
            taskMap.append("realBillNo", billNo);
            taskMap.remove(IVtuzxConst.Key_createTime);
            taskMap.remove(IVtuzxConst.Key_updateTime);
            taskMap.remove("_createUserId");
            taskMap.remove("createUserName");
            taskMap.remove("_updateUserId");
            taskMap.remove("updateUserName");
            dataList.add(taskMap);
            if (review) {
                VtuzxMap reMap = new VtuzxMap(taskMap);
                reMap.remove("billNo");
                reMap.append("billNo", "FP_" + billNo);
                reMap.append("inventoryType", "review");
                dataList.add(reMap);
            }
        }
        return new VtuzxMap(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public Object getUserTaskDetail(ObjectId userId, String name, String billNo, String stockNo, String locationNo) throws VtuzxException {
        Document locationDoc = stockDetailDao.findOne(Criteria.where("fNumber").is(stockNo).and("locationNo").is(locationNo));
        if (VtuzxUtil.isEmpty(locationDoc)) {
            throw new VtuzxException("仓位不存在");
        }
        Document taskDoc;
        boolean isFP = false;
        String subBillNo = billNo;
        if (billNo.contains("FP_")) {
            subBillNo = billNo.substring(3);
            isFP = true;
        }
        taskDoc = stockTakeTaskDao.findOne(Criteria.where("billNo").is(subBillNo));
        boolean stockTakeMethod = VtuzxUtil.getBoolean(taskDoc, "stockTakeMethod");
        List<VtuzxMap> detailList = new ArrayList<>();
        Criteria cond = Criteria.where("stockNo").is(stockNo).and("locationNo").is(locationNo);
        if (stockTakeMethod) {
            cond.and("count").gt(0);
        }
        Iterator<Document> detailDocItr = stockTakeTaskDetailDao.findAggregateList(isFP, subBillNo, cond, null, 0, 0);
        while (detailDocItr.hasNext()) {
            VtuzxMap data = new VtuzxMap();
            Document detailDoc = detailDocItr.next();
            String materialNo = VtuzxUtil.getString(detailDoc, "materialNo");
            if (!"03010101042".equals(materialNo)){
                String materialName = VtuzxUtil.getString(detailDoc, "materialName");
                String batchNo = VtuzxUtil.getString(detailDoc, "batchNo");
                String key = VtuzxUtil.getString(detailDoc, "key");
                double bookCount = VtuzxUtil.getDouble(detailDoc, "bookCount");
                int bookBoxCount = VtuzxUtil.getInt(detailDoc, "bookBoxCount");
                double count;
                int boxCount;
                if (isFP) {
                    count = VtuzxUtil.getDouble(detailDoc, "reviewCount");
                    boxCount = VtuzxUtil.getInt(detailDoc, "reviewBoxCount");
                    data.append("sortKey", (bookBoxCount - VtuzxUtil.getInt(detailDoc, "boxCount")) + 100 * (bookBoxCount - boxCount));
                } else {
                    count = VtuzxUtil.getDouble(detailDoc, "count");
                    boxCount = VtuzxUtil.getInt(detailDoc, "boxCount");
                    data.append("sortKey", bookBoxCount - boxCount);
                }
                data.append("materialName", materialName)
                        .append("materialNo", materialNo)
                        .append("batchNo", batchNo)
                        .append("locationNo", locationNo)
                        .append("key", key)
                        .append("bookCount", bookCount)
                        .append("bookBoxCount", bookBoxCount)
                        .append("count", count)
                        .append("boxCount", boxCount);
                detailList.add(data);
            }

        }

        detailList.sort((a, b) -> {
            int aSortKey = VtuzxUtil.getInt(a, "sortKey");
            int bSortKey = VtuzxUtil.getInt(b, "sortKey");

            return bSortKey - aSortKey;
        });

        return new VtuzxMap(IVtuzxConst.Key_data, detailList);
    }

    @Override
    public Object getUserTaskDetailBox(ObjectId userId, String name, String key, String billNo) {
        boolean isFP = false;
        String subBillNo = billNo;
        if (billNo.contains("FP_")) {
            subBillNo = billNo.substring(3);
            isFP = true;
        }
        List<VtuzxMap> boxList = new ArrayList<>();
        Document taskDoc = stockTakeTaskDao.findOne(Criteria.where("billNo").is(subBillNo));
        boolean stockTakeMethod = VtuzxUtil.getBoolean(taskDoc, "stockTakeMethod");
        Criteria cond = Criteria.where("key").is(key);
        if (stockTakeMethod) {
            cond.and("count").gt(0);
        }
        Iterator<Document> boxDocItr;

        if (isFP) {
            boxDocItr = stockTakeTaskDetailBoxDao.findList(StockTakeTaskDetailBoxDao.NAME + subBillNo, cond, new Document("inventoryStatus", 1).append("key", 1), 0, 0);
        } else {
            boxDocItr = stockTakeTaskDetailBoxDao.findList(StockTakeTaskDetailBoxDao.NAME + subBillNo, cond, new Document("status", 1).append("key", 1), 0, 0);
        }

        while (boxDocItr.hasNext()) {
            VtuzxMap boxMap = new VtuzxMap();
            Document boxDoc = boxDocItr.next();
            double bookCount = VtuzxUtil.getDouble(boxDoc, "bookCount");
            double count;
            if (billNo.contains("FP_")) {
                count = VtuzxUtil.getDouble(boxDoc, "reviewCount");
            } else {
                count = VtuzxUtil.getDouble(boxDoc, "count");
            }
            String status = VtuzxUtil.getString(boxDoc, "status");
            String userName = VtuzxUtil.getString(boxDoc, "userName");
            String userid = VtuzxUtil.getString(boxDoc, "userName");
            String materialName = VtuzxUtil.getString(boxDoc, "materialName");
            String materialNo = VtuzxUtil.getString(boxDoc, "materialNo");
            String batchNo = VtuzxUtil.getString(boxDoc, "batchNo");
            String boxNo = VtuzxUtil.getString(boxDoc, "boxNo");
            String locationNo = VtuzxUtil.getString(boxDoc, "locationNo");
            String materialSpec = VtuzxUtil.getString(boxDoc, "materialSpec");
            boxMap.append("materialName", materialName)
                    .append("materialNo", materialNo)
                    .append("batchNo", batchNo)
                    .append("boxNo", boxNo)
                    .append("locationNo", locationNo)
                    .append("materialSpec", materialSpec)
                    .append("bookCount", bookCount)
                    .append("count", count)
                    .append("status", status)
                    .append("userName", userName)
                    .append("userId", userid);
            boxList.add(boxMap);
        }
        return new VtuzxMap(IVtuzxConst.Key_data, boxList);
    }

    //TODO
    @Override
    public void addUserTaskDetailBox(ObjectId userId, String name, String boxNo, String locationNo, String billNo, double count) throws VtuzxException {
        Criteria cond = Criteria.where("boxNo").is(boxNo);
        Document boxDoc;
        if (billNo.contains("FP_")) {
            String subBillNo = billNo.substring(3);
            boxDoc = stockTakeTaskDetailBoxDao.findOne(StockTakeTaskDetailBoxDao.NAME + subBillNo, cond);
            if (VtuzxUtil.isEmpty(boxDoc)) {
                throw new VtuzxException(String.format("箱号[%s]不存在", boxNo));
            }
            String key = VtuzxUtil.getString(boxDoc, "key");
            ObjectId id = VtuzxUtil.getObject(boxDoc, IVtuzxConst.Key__id);
            Document boxDocument = new Document("reviewCount", count)
                    .append("reviewUserId", userId)
                    .append("reviewUserName", name)
                    .append("inventoryStatus", "review");
            stockTakeTaskDetailBoxDao.updateById(StockTakeTaskDetailBoxDao.NAME + subBillNo, id, boxDocument);
            updateDetailCount(billNo, key);
        } else {
            boxDoc = stockTakeTaskDetailBoxDao.findOne(StockTakeTaskDetailBoxDao.NAME + billNo, cond);
            if (VtuzxUtil.isEmpty(boxDoc)) {
                throw new VtuzxException(String.format("箱号[%s]不存在", boxNo));
            }
            String location = VtuzxUtil.getString(boxDoc, "locationNo");
            ObjectId id = VtuzxUtil.getObject(boxDoc, IVtuzxConst.Key__id);
            String key = VtuzxUtil.getString(boxDoc, "key");
            if (!location.equals(locationNo)) {
                stockTakeTaskDetailBoxDao.updateById(StockTakeTaskDetailBoxDao.NAME + billNo, id, new Document("count", 0).append("stockChange", "out"));
                Document doc = new Document(boxDoc);
                doc.remove("_id");
                doc.remove("updateTime");
                doc.remove("createTime");
                doc.remove("locationNo");
                doc.remove("bookCount");
                doc.append("locationNo", locationNo);
                doc.append("bookCount", 0);
                doc.append("stockChange", "in");
                id = stockTakeTaskDetailBoxDao.insertOne(StockTakeTaskDetailBoxDao.NAME + billNo, doc);
            }

            Document boxDocument = new Document("status", "yes")
                    .append("count", count)
                    .append("userId", userId)
                    .append("userName", name);
            stockTakeTaskDetailBoxDao.updateById(StockTakeTaskDetailBoxDao.NAME + billNo, id, boxDocument);
            updateDetailCount(billNo, key);
        }
    }

    /**
     * 更新分录的重量和箱数
     * @param billNo 单据号
     * @param key KEY
     */
    private void updateDetailCount(String billNo, String key) {
        if (billNo.contains("FP_")) {
            String subBillNo = billNo.substring(3);
            Iterator<Document> boxItr = stockTakeTaskDetailBoxDao.findList(StockTakeTaskDetailBoxDao.NAME + subBillNo, Criteria.where("key").is(key).and("inventoryStatus").is("review"), null, 0, 0);
            int boxCount = 0;
            double count = 0d;
            while(boxItr.hasNext()) {
                Document box = boxItr.next();
                boxCount++;
                count += VtuzxUtil.getDouble(box, "reviewCount");
            }
            stockTakeTaskDetailDao.updateOne(StockTakeTaskDetailDao.NAME + subBillNo, Criteria.where("key").is(key), new Document("reviewBoxCount", boxCount).append("reviewCount", count).append("inventoryStatus", "review"));
        } else {
            Iterator<Document> boxItr = stockTakeTaskDetailBoxDao.findList(StockTakeTaskDetailBoxDao.NAME + billNo, Criteria.where("key").is(key).and("status").is("yes"), null, 0, 0);
            int boxCount = 0;
            double count = 0d;
            while(boxItr.hasNext()) {
                Document box = boxItr.next();
                boxCount++;
                count += VtuzxUtil.getDouble(box, "count");
            }
            stockTakeTaskDetailDao.updateOne(StockTakeTaskDetailDao.NAME + billNo, Criteria.where("key").is(key), new Document("boxCount", boxCount).append("count", count));
        }
    }

    @Override
    public void addUserTaskDetailBoxList(ObjectId userId, String name, List<Map<String, Object>> boxList, String locationNo, String billNo) throws VtuzxException {
        for (Map<String, Object> box: boxList) {
            String boxNo = VtuzxUtil.getString(box, "boxNo");
            double count = VtuzxUtil.getDouble(box, "count");
            Criteria cond = Criteria.where("boxNo").is(boxNo);
            Document boxDoc;
            if (billNo.contains("FP_")) {
                String subBillNo = billNo.substring(3);
                boxDoc = stockTakeTaskDetailBoxDao.findOne(StockTakeTaskDetailBoxDao.NAME + subBillNo, cond);
                if (VtuzxUtil.isEmpty(boxDoc)) {
                    throw new VtuzxException(String.format("箱号[%s]不存在", boxNo));
                }
                ObjectId id = VtuzxUtil.getObject(boxDoc, IVtuzxConst.Key__id);
                String key = VtuzxUtil.getString(boxDoc, "key");
//                String status = VtuzxUtil.getString(boxDoc, "status");
//                ObjectId inventoryUserId = VtuzxUtil.getObject(boxDoc, "userId");
//                if (!"yes".equals(status)) {
//                    throw new VtuzxException("未盘过的作业不能进行复盘！");
//                }
//                if (inventoryUserId.equals(userId)) {
//                    throw new VtuzxException("首盘和复盘不能为同一人！");
//                }
                Document boxDocument = new Document("reviewCount", count)
                        .append("reviewUserId", userId)
                        .append("reviewUserName", name)
                        .append("inventoryStatus", "review");
                stockTakeTaskDetailBoxDao.updateById(StockTakeTaskDetailBoxDao.NAME + subBillNo, id, boxDocument);
                updateDetailCount(billNo, key);
            } else {
                boxDoc = stockTakeTaskDetailBoxDao.findOne(StockTakeTaskDetailBoxDao.NAME + billNo, cond);
                if (VtuzxUtil.isEmpty(boxDoc)) {
                    throw new VtuzxException(String.format("箱号[%s]不存在", boxNo));
                }
                String location = VtuzxUtil.getString(boxDoc, "locationNo");
                ObjectId id = VtuzxUtil.getObject(boxDoc, IVtuzxConst.Key__id);
                String key = VtuzxUtil.getString(boxDoc, "key");
                if (!location.equals(locationNo)) {
                    stockTakeTaskDetailBoxDao.updateById(StockTakeTaskDetailBoxDao.NAME + billNo, id, new Document("count", 0).append("stockChange", "out"));
                    Document doc = new Document(boxDoc);
                    doc.remove("_id");
                    doc.remove("updateTime");
                    doc.remove("createTime");
                    doc.remove("locationNo");
                    doc.remove("bookCount");
                    doc.append("locationNo", locationNo);
                    doc.append("bookCount", 0);
                    doc.append("stockChange", "in");
                    id = stockTakeTaskDetailBoxDao.insertOne(StockTakeTaskDetailBoxDao.NAME + billNo, doc);
                }
                Document boxDocument = new Document("status", "yes")
                        .append("count", count)
                        .append("userId", userId)
                        .append("userName", name);
                stockTakeTaskDetailBoxDao.updateById(StockTakeTaskDetailBoxDao.NAME + billNo, id, boxDocument);
                updateDetailCount(billNo, key);
            }
        }

    }

    @Override
    public void deleteUserDetailBox(ObjectId userId, String name, String boxNo, String billNo) {
        if (billNo.contains("FP_")) {
            String subBillNo = billNo.substring(3);
            Document boxDoc = stockTakeTaskDetailBoxDao.findOne(StockTakeTaskDetailBoxDao.NAME + subBillNo, Criteria.where("boxNo").is(boxNo));
            ObjectId id = VtuzxUtil.getObject(boxDoc, IVtuzxConst.Key__id);
            stockTakeTaskDetailBoxDao.updateByIdRedirect(StockTakeTaskDetailBoxDao.NAME + subBillNo, id, new Document("$unset", new Document("reviewCount", 1)
                    .append("inventoryStatus", 1)
                    .append("reviewUserId", 1)
                    .append("reviewUserName", 1)));
            String key = VtuzxUtil.getString(boxDoc, "key");

            updateDetailCount(billNo, key);
        } else {
            Document boxDoc = stockTakeTaskDetailBoxDao.findOne(StockTakeTaskDetailBoxDao.NAME + billNo, Criteria.where("boxNo").is(boxNo));
            ObjectId id = VtuzxUtil.getObject(boxDoc, IVtuzxConst.Key__id);
            stockTakeTaskDetailBoxDao.updateByIdRedirect(StockTakeTaskDetailBoxDao.NAME + billNo, id, new Document("$unset", new Document("count", 1)
                    .append("status", 1)
                    .append("userId", 1)
                    .append("userName", 1)));
            String key = VtuzxUtil.getString(boxDoc, "key");

            updateDetailCount(billNo, key);
        }
    }

    @Override
    public void deleteUserDetail(ObjectId userId, String name, String key, String billNo) {
        if (billNo.contains("FP_")) {
            String subBillNo = billNo.substring(3);
            stockTakeTaskDetailDao.updateOneRedirect(StockTakeTaskDetailDao.NAME + subBillNo, Criteria.where("key").is(key),
                    new Document("$unset",
                            new Document("reviewCount", 1)
                                    .append("reviewBoxCount", 1)
                                    .append("review", 1)));
            stockTakeTaskDetailBoxDao.updateManyRedirect(StockTakeTaskDetailBoxDao.NAME + subBillNo, Criteria.where("key").is(key).and("inventoryStatus").is("review"),
                    new Document("$unset", new Document("reviewCount", 1)
                            .append("inventoryStatus", 1)
                            .append("reviewUserId", 1)
                            .append("reviewUserName", 1)));
        } else {
            stockTakeTaskDetailDao.updateOneRedirect(StockTakeTaskDetailDao.NAME + billNo, Criteria.where("key").is(key),
                    new Document("$unset",
                            new Document("count", 1)
                                    .append("boxCount", 1)
                                    .append("status", 1)));
            stockTakeTaskDetailBoxDao.updateManyRedirect(StockTakeTaskDetailBoxDao.NAME + billNo, Criteria.where("key").is(key).and("status").is("yes"),
                    new Document("$unset", new Document("count", 1)
                            .append("detailStatus", 1)
                            .append("userId", 1)
                            .append("userName", 1)));
        }
    }

    @Override
    public void saveUserTask(ObjectId userId, String name, String billNo, String stockNo, String locationNo) {
        boolean isFP = false;
        String subBillNo = billNo;
        if (billNo.contains("FP_")) {
            isFP = true;
            subBillNo = billNo.substring(3);
        }

        Criteria detailCond = Criteria.where("stockNo").is(stockNo).and("locationNo").is(locationNo);
        stockTakeTaskDetailDao.saveBill(isFP, subBillNo, detailCond, "save");


        Criteria boxCond = Criteria.where("stockNo").is(stockNo).and("locationNo").is(locationNo);
        stockTakeTaskDetailBoxDao.saveBill(isFP, subBillNo, boxCond, "save");

        Iterator<Document> boxItr = stockTakeTaskDetailBoxDao.findList(StockTakeTaskDetailBoxDao.NAME + subBillNo, Criteria.where(isFP? "inventoryDetailStatus": "detailStatus").is("save"), null, 0, 0);
        VtuzxMap boxMap = FastUtil.itr2Map(boxItr, "boxNo");
        int sweptCount = boxMap.size();

        Document taskDoc = stockTakeTaskDao.findOne(Criteria.where("billNo").is(subBillNo));
        boolean stockTakeMethod = VtuzxUtil.getBoolean(taskDoc, "stockTakeMethod");
        if (!stockTakeMethod) {
            stockTakeTaskDao.updateById(taskDoc.getObjectId(IVtuzxConst.Key__id), new Document(isFP? "reviewSweptCount": "sweptCount", sweptCount));
        }

    }

    @Override
    public VtuzxMap getMaterialBox(String billNo, String stockNo, String locationNo, String boxNoKey) throws VtuzxException {
        String boxNo;
        boolean isBC = false;
        if (boxNoKey.contains("|;")) {
            String[] boxValues = boxNoKey.split("\\|\\;");
            if (boxValues.length == 5) {
                boxNo = boxNoKey;
                boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
                boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
                boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
                boxNo = boxNo + "|;batch";
                isBC = true;
            } else {
                boxNo = boxValues[boxValues.length - 1];
            }
        } else {
            boxNo = boxNoKey;
        }
        String subBillNo = billNo;
        if (billNo.contains("FP_")) {
            subBillNo = billNo.substring(3);
        }
        Criteria cond = Criteria.where("boxNo").is(boxNo).and("locationNo").is(locationNo).and("stockNo").is(stockNo);
        // 箱号存在校验
        Document stBoxDetail = stockTakeTaskDetailBoxDao.findOne(StockTakeTaskDetailBoxDao.NAME + subBillNo, cond);
        if (stBoxDetail == null) {
            //如果当前箱号不在这个仓库,查询及时库存看是否有这个箱号
            Document inventoryDoc = inventoryDetailDao.findOne(Criteria.where("boxNo").is(boxNo));
            if (VtuzxUtil.isEmpty(inventoryDoc)){
                throw new VtuzxException("箱码"+ boxNo +"不存在");
            } else {//如果在即时库存有,判断是否出库,判断是哪个仓库的
                String status = VtuzxUtil.getString(inventoryDoc,"status");
                if ("out".equals(status)){
                    throw new VtuzxException("箱码"+ boxNo +"已出库");
                } else {//如果还在库判断在那个仓库
                    String inStockNo = VtuzxUtil.getString(inventoryDoc,"stockNo");
                    Document stockDoc = stockDao.findOne(Criteria.where("fNumber").is(inStockNo));
                    String stockName = VtuzxUtil.getString(stockDoc,"fName");
                    throw new VtuzxException("箱码"+ boxNo +",在" + stockName);
                }

            }
            //throw new VtuzxException(IXqcConst.Msg_E_XQC_box_not_in_inventory, boxNo);
        }
        VtuzxMap result = new VtuzxMap(stBoxDetail);
        double bookCount = VtuzxUtil.getDouble(result, "bookCount");
        result.remove(IVtuzxConst.Key__id);
        result.remove(IVtuzxConst.Key_createTime);
        result.remove(IVtuzxConst.Key_updateTime);
        result.append("count", bookCount);
        if (isBC) {
            result.append("barcodeType", "BC");
        }
        return result;
    }

    @Override
    public VtuzxMap getMaterialByRfidInStock(String billNo, List<String> rfidList, String stockNo, String locationNo) {
        String subBillNo = billNo;
        if (billNo.contains("FP_")) {
            subBillNo = billNo.substring(3);
        }
        Criteria cond = Criteria.where("rfid").in(rfidList).and("stockNo").is(stockNo).and("locationNo").is(locationNo);
        Iterator<Document> boxItr = stockTakeTaskDetailBoxDao.findList(StockTakeTaskDetailBoxDao.NAME + subBillNo, cond, null, 0, 0);

        List<VtuzxMap> retList = new ArrayList<>();
        while(boxItr.hasNext()) {
            Document box = boxItr.next();
            VtuzxMap retBox = new VtuzxMap(box);
            double bookCount = VtuzxUtil.getDouble(retBox, "bookCount");
            retBox.remove(IVtuzxConst.Key__id);
            retBox.remove(IVtuzxConst.Key_createTime);
            retBox.remove(IVtuzxConst.Key_updateTime);
            retBox.append("count", bookCount);
            retList.add(retBox);
        }
        return new VtuzxMap(IVtuzxConst.Key_data, retList);
    }

    @Override
    public void toTPALOrder(ObjectId userId, String name, String billNo) throws VtuzxException {
        Document profitAndLossDoc = profitAndLossDao.findOne(Criteria.where("taskBillNo").is(billNo));
        if (!VtuzxUtil.isEmpty(profitAndLossDoc)) {
            throw new VtuzxException("不允许重复生成盘盈亏单");
        }
        //盘盈单
        Iterator<Document> detailBoxItr = stockTakeTaskDetailBoxDao.findList(StockTakeTaskDetailBoxDao.NAME + billNo, Criteria.where("billNo").is(billNo).and("TPALCount").gt(0), null, 0, 0);
        TPALOrderInner(detailBoxItr, userId, name, billNo, true);
        //盘亏单
        Iterator<Document> unDetailBoxItr = stockTakeTaskDetailBoxDao.findList(StockTakeTaskDetailBoxDao.NAME + billNo, Criteria.where("billNo").is(billNo).and("TPALCount").lt(0), null, 0, 0);
        TPALOrderInner(unDetailBoxItr, userId, name, billNo, false);


    }

    /**
     * 生成盘盈亏核心
     * @param detailBoxItr
     * @param userId
     * @param name
     * @param billNo
     * @param profit
     */
    private void TPALOrderInner(Iterator<Document> detailBoxItr, ObjectId userId, String name, String billNo, boolean profit) {
        VtuzxMap orderMap = new VtuzxMap();
        while (detailBoxItr.hasNext()) {
            Document detailBoxDoc = detailBoxItr.next();
            String orderKey = VtuzxUtil.getString(detailBoxDoc, "stockNo");
            VtuzxMap groupMap = VtuzxUtil.getObject(orderMap, orderKey);
            if (CollectionUtils.isEmpty(groupMap)) {
                groupMap = new VtuzxMap();
            }
            String locationNo = VtuzxUtil.getString(detailBoxDoc, "locationNo");
            String batchNo = VtuzxUtil.getString(detailBoxDoc, "batchNo");
            String materialNo = VtuzxUtil.getString(detailBoxDoc, "materialNo");

            String key = locationNo + "," + batchNo + "," + materialNo;
            List<Document> arr = VtuzxUtil.getObject(groupMap, key);
            if (CollectionUtils.isEmpty(arr)) {
                arr = new ArrayList<>();
            }
            arr.add(detailBoxDoc);
            groupMap.append(key, arr);
            orderMap.append(orderKey, groupMap);
        }
        orderMap.forEach((key, val) -> {
            List<Document> groupList = new ArrayList<>();
            List<Document> boxList = new ArrayList<>();
            VtuzxMap groupMap = (VtuzxMap) val;
            String pykdBillNo;
            if (profit) {
                pykdBillNo = IXqcConst.createBillNo(BillType.PYD);
            } else {
                pykdBillNo = IXqcConst.createBillNo(BillType.PKD);
            }
            Document orderDoc = new Document("billNo", pykdBillNo);
            orderDoc.append("stockNo", key);
            orderDoc.append("taskBillNo", billNo);
            orderDoc.append("userName", name);
            orderDoc.append("userId", userId);
            orderDoc.append("status", BillStatus.Save.toString());
            orderDoc.append("createDate", VtuzxUtil.formatYmdF(new Date()));
            ObjectId pykdId = profitAndLossDao.insertBill(userId, name, orderDoc);

            groupMap.forEach((groupKey, groupVal) -> {
                List<Document> detailList = (List<Document>) groupVal;
                Document groupDoc = new Document();
                List<String> list = VtuzxUtil.split(groupKey, ",");
                if (VtuzxUtil.isEmpty(list) || list.size() != 3) {
                    return;
                }
                String locationNo = list.get(0);
                String batchNo = list.get(1);
                Document materialDoc = materialDao.findOne(Criteria.where("fNumber").is(list.get(2)));
                String materialNo = VtuzxUtil.getString(materialDoc, "fNumber");
                String materialName = VtuzxUtil.getString(materialDoc, "fName");
                String materialSpec = VtuzxUtil.getString(materialDoc, "fSpecification");
                String unit = VtuzxUtil.getString(materialDoc, "fBaseUnitIdNumber");
                String unitName = VtuzxUtil.getString(materialDoc, "fBaseUnitIdFName");
                if (!VtuzxUtil.isEmpty(list)) {
                    groupDoc.append("locationNo", locationNo);
                    groupDoc.append("materialNo", materialNo);
                    groupDoc.append("batchNo", batchNo);
                }
                groupDoc.append("materialName", materialName);
                groupDoc.append("materialSpec", materialSpec);
                groupDoc.append("unit", unit);
                groupDoc.append("unitName", unitName);
                groupDoc.append("_pykdId", pykdId);
                groupDoc.append("TPALkey", groupKey);
                groupDoc.append("billNo", pykdBillNo);

                int detailBoxCount = 0;
                double detailCount = 0d;
                for (Document detail: detailList) {
                    Document boxDoc = new Document(detail);
                    double count;
                    if (profit) {
                        count = VtuzxUtil.getDouble(boxDoc, "TPALCount");
                    } else {
                        count = Math.abs(VtuzxUtil.getDouble(boxDoc, "TPALCount"));
                    }
                    detailCount += count;
                    detailBoxCount++;
                    boxDoc.append("TPALkey", groupKey);
                    boxDoc.remove(IVtuzxConst.Key__id);
                    boxDoc.remove(IVtuzxConst.Key_createTime);
                    boxDoc.remove(IVtuzxConst.Key_updateTime);
                    boxDoc.append("billNo", pykdBillNo);
                    boxList.add(boxDoc);
                }
                if (profit) {
                    groupDoc.append("count", detailCount);
                    groupDoc.append("boxCount", detailBoxCount);
                } else {
                    groupDoc.append("count", "-" + detailCount);
                    groupDoc.append("boxCount", "-" + detailBoxCount);
                }
                groupList.add(groupDoc);
            });
            profitAndLossDetailBoxDao.insertMany(boxList);
            profitAndLossDetailDao.insertMany(groupList);
        });
    }


    @Override
    public void auditProfitAndLoss(ObjectId _userId, String userName, Map<String, Object> param) throws VtuzxException {
        String stockNo = VtuzxUtil.getString(param, "stockNo");
        String billNo = VtuzxUtil.getString(param, "billNo");
        Criteria getCond = Criteria.where("billNo").is(billNo);
        Iterator<Document> profitAndLossDetailItr = profitAndLossDetailDao.findList(getCond, null, 0, 0);
        Iterator<Document> profitAndLossDetailBoxItr = profitAndLossDetailBoxDao.findList(getCond, null, 0, 0);
        List<Document> profitAndLossDetailBoxList = new ArrayList<>();
        while(profitAndLossDetailBoxItr.hasNext()) {
            profitAndLossDetailBoxList.add(profitAndLossDetailBoxItr.next());
        }
        List<BillMaterial> profitAndLossDetailList = new ArrayList<>();
        List<String> boxNoList = new ArrayList<>();
        List<VtuzxMap> bcBoxList = new ArrayList<>();
        while(profitAndLossDetailItr.hasNext()) {
            Document profitAndLossDetail = profitAndLossDetailItr.next();
            BillMaterial billMaterial = new BillMaterial();
            String detailTPALkey = VtuzxUtil.getString(profitAndLossDetail, "TPALkey");
            billMaterial.setMaterialNo(VtuzxUtil.getString(profitAndLossDetail, "materialNo"));
            billMaterial.setUnit(VtuzxUtil.getString(profitAndLossDetail, "unit"));
            billMaterial.setBatchNo(VtuzxUtil.getString(profitAndLossDetail, "batchNo"));
            billMaterial.setProduceDate(VtuzxUtil.getString(profitAndLossDetail, "produceDate"));
            billMaterial.setExp(VtuzxUtil.getInt(profitAndLossDetail, "exp"));
            List<BillMaterialBox> boxList = new ArrayList<>();
            for(Document boxDoc: profitAndLossDetailBoxList) {
                String boxTPALkey = VtuzxUtil.getString(boxDoc, "TPALkey");
                if (detailTPALkey.equals(boxTPALkey)) {
                    BillMaterialBox box = new BillMaterialBox();
                    String boxNo = VtuzxUtil.getString(boxDoc, "boxNo");
                    box.setBoxNo(boxNo);
                    box.setRfid(VtuzxUtil.getString(boxDoc, "rfid"));
                    double count = boxDoc.containsKey("TPALCount")? (-1 * VtuzxUtil.getDouble(boxDoc, "TPALCount")): VtuzxUtil.getDouble(boxDoc, "count");
                    box.setCount(count);
                    box.setLocationNo(VtuzxUtil.getString(profitAndLossDetail, "locationNo"));
                    boxList.add(box);
                    String[] boxValues = boxNo.split("\\|\\;");
                    if (boxValues.length == 3) {
                        // 已入库包材长度为3
                        bcBoxList.add(new VtuzxMap("boxNo", boxNo).append("count", BigDecimal.valueOf(count).setScale(2, RoundingMode.HALF_UP).doubleValue()));
                    } else {
                        boxNoList.add(boxNo);
                    }
                }
                billMaterial.setBoxList(boxList);
            }
            profitAndLossDetailList.add(billMaterial);
        }
        profitAndLossDao.audit(_userId, userName, billNo);

        // 更新即时库存
        try {
            if (billNo.contains("PYD")) {
                inventoryService.addInventory(_userId, userName, BillEvent.Audit, BillType.PDRK, stockNo, billNo, profitAndLossDetailList);
            } else {
                inventoryService.reduceInventory(_userId, userName, BillEvent.Audit, BillType.PDCK, stockNo, billNo, boxNoList, bcBoxList);
            }
        } catch(Exception e) {
            profitAndLossDao.unaudit(billNo);
            throw e;
        }
    }

    @Override
    public void unauditProfitAndLoss(ObjectId userId, String name, Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");
        profitAndLossDao.unaudit(billNo);
    }

    @Override
    public VtuzxMap getProfitAndLossList(ObjectId userId, Map<String, Object> param) {
        int pageIndex = VtuzxUtil.getInt(param, IVtuzxConst.Key_pageIndex);
        int pageSize = VtuzxUtil.getInt(param, IVtuzxConst.Key_pageSize);
        String billNo = VtuzxUtil.getString(param, "billNo");
        List<String> createDate = VtuzxUtil.getObject(param, "createDate");
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(billNo)) {
            cond.and("billNo").regex(billNo);
        }
        if (!VtuzxUtil.isEmpty(createDate)) {
            cond.and("createDate").gte(VtuzxUtil.parseDate(createDate.get(0))).lte(VtuzxUtil.parseDate(createDate.get(1)));
        }
        VtuzxPage page = profitAndLossDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        List<VtuzxMap> dataList = page.iterator(doc -> new VtuzxMap("id", VtuzxUtil.getString(doc, IVtuzxConst.Key__id))
                .append("billNo", VtuzxUtil.getString(doc, "billNo"))
                .append("stockNo", VtuzxUtil.getString(doc, "stockNo"))
                .append("userName", VtuzxUtil.getString(doc, "userName"))
                .append("userId", VtuzxUtil.getString(doc, "userId"))
                .append("status", VtuzxUtil.getString(doc, "status"))
                .append("createDate", VtuzxUtil.getString(doc, "createDate"))
                .append("createUserName", VtuzxUtil.getString(doc, "createUserName")));
        return new VtuzxMap(IVtuzxConst.Key_data, dataList).append(IVtuzxConst.Key_total, page.total());
    }

    @Override
    public VtuzxMap getProfitAndLossDetail(ObjectId userId, String name, Map<String, Object> param) {
        String id = VtuzxUtil.getString(param, "id");
        List<VtuzxMap> dataList = new ArrayList<>();
        Document profitAndLossDoc = profitAndLossDao.findById(new ObjectId(id));
        VtuzxMap header = new VtuzxMap(profitAndLossDoc);
        header.append("id", VtuzxUtil.removeString(header, "_id"));
        header.remove("_createUserId");
        header.remove("createUserName");
        header.remove("_updateUserId");
        header.remove("updateUserName");
        header.remove("updateTime");
        header.remove("createTime");

        Iterator<Document> detailItr = profitAndLossDetailDao.findList(Criteria.where("_pykdId").is(new ObjectId(id)), null, 0, 0);
        while (detailItr.hasNext()) {
            Document detailDoc = detailItr.next();
            VtuzxMap detailMap = new VtuzxMap(detailDoc);
            detailMap.remove(IVtuzxConst.Key__id);
            detailMap.remove(IVtuzxConst.Key_updateTime);
            detailMap.remove(IVtuzxConst.Key_createTime);
            dataList.add(detailMap);
        }
        return new VtuzxMap("header", header).append(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public VtuzxMap getProfitAndLossDetailBox(ObjectId userId, String name, Map<String, Object> param) {
        List<VtuzxMap> dataList = new ArrayList<>();
        String billNo = VtuzxUtil.getString(param, "billNo");
        String key = VtuzxUtil.getString(param, "key");
        Iterator<Document> boxItr = profitAndLossDetailBoxDao.findList(Criteria.where("TPALkey").is(key).and("billNo").is(billNo), null, 0, 0);
        while (boxItr.hasNext()) {
            Document boxDoc = boxItr.next();
            VtuzxMap detailMap = new VtuzxMap(boxDoc);
            detailMap.remove(IVtuzxConst.Key__id);
            detailMap.remove(IVtuzxConst.Key_updateTime);
            detailMap.remove(IVtuzxConst.Key_createTime);
            dataList.add(detailMap);
        }
        return new VtuzxMap(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public VtuzxMap getLocationList(ObjectId userId, String name, String stockNo) {
        List<VtuzxMap> dataList = new ArrayList<>();
        Iterator<Document> locationItr = stockDetailDao.findList(Criteria.where("fNumber").is(stockNo), new Document("locationNo", 1), 0, 0);
        while (locationItr.hasNext()) {
            Document locationDoc = locationItr.next();
            VtuzxMap locationMap = new VtuzxMap(locationDoc);
            locationMap.remove(IVtuzxConst.Key_createTime);
            locationMap.remove(IVtuzxConst.Key_updateTime);
            locationMap.remove(IVtuzxConst.Key__id);
            dataList.add(locationMap);
        }
        return new VtuzxMap(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public VtuzxMap getRfidCount(ObjectId userId, String name, List<String> rfidList, String billNo, String locationNo) {
        boolean isFP = false;
        String subBillNo = billNo;
        if (billNo.contains("FP_")) {
            subBillNo = billNo.substring(3);
            isFP = true;
        }

        Criteria boxCond = Criteria.where("rfid").in(rfidList).and("locationNo").is(locationNo);
        if (isFP) {
            boxCond.and("inventoryStatus").is(null);
        } else {
            boxCond.and("status").is(null);
        }

        Iterator<Document> boxItr = stockTakeTaskDetailBoxDao.findList(StockTakeTaskDetailBoxDao.NAME + subBillNo, boxCond, null, 0,0);
        VtuzxMap boxMap = FastUtil.itr2Map(boxItr, "boxNo");
        int boxCount = boxMap.size();

        Criteria boxCountCond = Criteria.where("locationNo").is(locationNo);
        Document boxCountDoc = stockTakeTaskDetailBoxDao.statBoxCount(subBillNo, boxCountCond);
        int sumBoxCount = 0;
        if (!VtuzxUtil.isEmpty(boxCountDoc)) {
            sumBoxCount = VtuzxUtil.getInt(boxCountDoc, "scanBoxCount");
        }

        Criteria cond = Criteria.where("locationNo").is(locationNo);
        if (isFP) {
            cond.and("inventoryStatus").is("review").and("status").is("yes");
        } else {
            cond.and("status").is("yes");
        }
        Document boxDoc = stockTakeTaskDetailBoxDao.statBoxCount(subBillNo, cond);
        int scanBoxCount = 0;
        if (!VtuzxUtil.isEmpty(boxDoc)) {
            scanBoxCount = VtuzxUtil.getInt(boxDoc, "scanBoxCount");
        }
        int nSweptBoxCount = sumBoxCount - scanBoxCount;


        return new VtuzxMap("boxCount", boxCount).append("nSweptBoxCount", nSweptBoxCount);
    }

    @Override
    public Object getBoxNoCount(ObjectId userId, String name, List<String> boxNoList, String billNo, String locationNo) {
        boolean isFP = false;
        String subBillNo = billNo;
        if (billNo.contains("FP_")) {
            subBillNo = billNo.substring(3);
            isFP = true;
        }
        Criteria boxCond = new Criteria("boxNo").in(boxNoList).and("locationNo").is(locationNo);
        if (isFP) {
            boxCond.and("inventoryStatus").is(null);
        } else {
            boxCond.and("status").is(null);
        }
        Iterator<Document> boxItr = stockTakeTaskDetailBoxDao.findList(StockTakeTaskDetailBoxDao.NAME + subBillNo, boxCond, null, 0,0);
        VtuzxMap boxMap = FastUtil.itr2Map(boxItr, "boxNo");
        int boxCount = boxMap.size();

        Criteria boxCountCond = Criteria.where("locationNo").is(locationNo);
        Document boxCountDoc = stockTakeTaskDetailBoxDao.statBoxCount(subBillNo, boxCountCond);
        int sumBoxCount = 0;
        if (!VtuzxUtil.isEmpty(boxCountDoc)) {
            sumBoxCount = VtuzxUtil.getInt(boxCountDoc, "scanBoxCount");
        }

        Criteria cond = Criteria.where("locationNo").is(locationNo);
        if (isFP) {
            cond.and("inventoryStatus").is("review").and("status").is("yes");
        } else {
            cond.and("status").is("yes");
        }
        Document boxDoc = stockTakeTaskDetailBoxDao.statBoxCount(subBillNo, cond);
        int scanBoxCount = 0;
        if (!VtuzxUtil.isEmpty(boxDoc)) {
            scanBoxCount = VtuzxUtil.getInt(boxDoc, "scanBoxCount");
        }
        int nSweptBoxCount = sumBoxCount - scanBoxCount;


        return new VtuzxMap("boxCount", boxCount).append("nSweptBoxCount", nSweptBoxCount);
    }

    @Override
    public VtuzxMap getBoxCount(ObjectId userId, String name, Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");
        boolean isFP = false;
        String subBillNo = billNo;
        if (billNo.contains("FP_")) {
            isFP = true;
            subBillNo = billNo.substring(3);
        }
        String locationNo = VtuzxUtil.getString(param, "locationNo");
        int locationScanBoxCount = 0;
        int locationSumBoxCount = 0;
        int locationNotSweptBoxCount = 0;
        AtomicInteger notSweepSum = new AtomicInteger();
        if (!VtuzxUtil.isEmpty(locationNo)) {
            Criteria locationCond = Criteria.where("locationNo").is(locationNo).and("materialNo").ne("03010101042");
            Document locationBoxDoc = stockTakeTaskDetailBoxDao.statBoxCount(subBillNo, locationCond);
            //仓位下总箱数
            if (!VtuzxUtil.isEmpty(locationBoxDoc)) {
                locationSumBoxCount = VtuzxUtil.getInt(locationBoxDoc, "scanBoxCount");
            }

            Criteria cond = Criteria.where("locationNo").is(locationNo);
            if (isFP) {
                cond.and("inventoryStatus").is("review");
            } else {
                cond.and("status").is("yes");
            }
            Document boxDoc = stockTakeTaskDetailBoxDao.statBoxCount(subBillNo, cond);
            //仓位下已扫数
            if (!VtuzxUtil.isEmpty(boxDoc)) {
                locationScanBoxCount = VtuzxUtil.getInt(boxDoc, "scanBoxCount");
            }
            locationNotSweptBoxCount = locationSumBoxCount - locationScanBoxCount;


            Criteria detailBoxCond = Criteria.where("locationNo").is(locationNo);
            detailBoxCond.orOperator(Criteria.where("status").ne("yes"), Criteria.where("inventoryStatus").ne("review"));
            long detailBoxTotal = stockTakeTaskDetailBoxDao.count(StockTakeTaskDetailBoxDao.NAME + subBillNo, detailBoxCond);
            notSweepSum.addAndGet((int)detailBoxTotal);
//            VtuzxMap boxMap = FastUtil.itr2Map(boxItr, "_id");
//            while (boxItr.hasNext()) {
//            boxMap.forEach((key, value) -> {
//                Document valueDoc = boxItr.next();
//                _logger.info(i + " & 000000000000");

//                String status = VtuzxUtil.getString(valueDoc, "status");
//                String inventoryStatus = VtuzxUtil.getString(valueDoc, "inventoryStatus");
//                if (!"yes".equals(status) || !"review".equals(inventoryStatus)) {
//                    notSweepSum.getAndIncrement();
//                }
//            }
        }

        Document taskDoc = stockTakeTaskDao.findOne(Criteria.where("billNo").is(subBillNo));
        //盘点作业取总箱数
        int sumBoxCount = VtuzxUtil.getInt(taskDoc, "sumBoxCount");
        Criteria cond = new Criteria();
        if (isFP) {
            cond.and("inventoryStatus").is("review");
        } else {
            cond.and("status").is("yes");
        }
        Document boxDoc = stockTakeTaskDetailBoxDao.statBoxCount(subBillNo, cond);
        //已扫数
        int scanBoxCount = 0;
        if (!VtuzxUtil.isEmpty(boxDoc)) {
            scanBoxCount = VtuzxUtil.getInt(boxDoc, "scanBoxCount");
        }
        //未扫数
        int notSweptBoxCount = sumBoxCount - scanBoxCount;

        return new VtuzxMap("sumBoxCount", sumBoxCount)
                .append("scanBoxCount", scanBoxCount)
                .append("notSweptBoxCount", notSweptBoxCount)
                .append("locationSumBoxCount", locationSumBoxCount)
                .append("locationScanBoxCount", locationScanBoxCount)
                .append("locationNotSweptBoxCount", locationNotSweptBoxCount)
                .append("notSweepSum", notSweepSum);
    }

    @Override
    public VtuzxDownloadBean downloadProfitAndLoss(ObjectId userId, String name, String _id, String billNo) throws Exception {
        BillType billType;
        if (billNo.contains("PYD")) {
            billType = BillType.PYD;
        } else {
            billType = BillType.PKD;
        }

        VtuzxMap map = downloadProfitAndLossInner(_id);

        List<VtuzxMap> detailList = new ArrayList<>();
        detailList.add(map);
        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());

        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "盘盈亏列表");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++, "单号"),
                new VtuzxExcelCell(col++, "仓库名称"),
                new VtuzxExcelCell(col++, "盘点日期"),
                new VtuzxExcelCell(col++, "库管"),
                new VtuzxExcelCell(col++, "仓位"),
                new VtuzxExcelCell(col++, "物料编码"),
                new VtuzxExcelCell(col++, "物料名称"),
                new VtuzxExcelCell(col++, "物料规格"),
                new VtuzxExcelCell(col++, "单位"),
                new VtuzxExcelCell(col++, "批次"),
                new VtuzxExcelCell(col++, "箱号"),
                new VtuzxExcelCell(col, "重量")
        ));
        sheet0.addRow(header0);

        for(VtuzxMap detail: detailList) {
            VtuzxMap header = VtuzxUtil.getObject(detail, "header");
            List<VtuzxMap> boxList = VtuzxUtil.getObject(detail, "data");

            String stockName = VtuzxUtil.getString(header, "stockName");
            String TPALBillNo = VtuzxUtil.getString(header, "billNo");
            String createDate = VtuzxUtil.getString(header, "createDate");
            String userName = VtuzxUtil.getString(header, "userName");
            for(VtuzxMap data: boxList) {
                String locationNo = VtuzxUtil.getString(data, "locationNo");
                String materialNo = VtuzxUtil.getString(data, "materialNo");
                String materialSpec = VtuzxUtil.getString(data, "materialSpec");
                String materialName = VtuzxUtil.getString(data, "materialName");
                String unitName = VtuzxUtil.getString(data, "unitName");
                String batchNo = VtuzxUtil.getString(data, "batchNo");
                String boxNo = VtuzxUtil.getString(data, "boxNo");
                String count = VtuzxUtil.getString(data, "TPALCount");
                col = 0;
                VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                        new VtuzxExcelCell(col++, TPALBillNo),
                        new VtuzxExcelCell(col++, stockName),
                        new VtuzxExcelCell(col++, createDate),
                        new VtuzxExcelCell(col++, userName),
                        new VtuzxExcelCell(col++, locationNo),
                        new VtuzxExcelCell(col++, materialNo),
                        new VtuzxExcelCell(col++, materialName),
                        new VtuzxExcelCell(col++, materialSpec),
                        new VtuzxExcelCell(col++, unitName),
                        new VtuzxExcelCell(col++, batchNo),
                        new VtuzxExcelCell(col++, boxNo),
                        new VtuzxExcelCell(col, count)
                ));
                sheet0.addRow(dataRow);
            }
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[] { 0, 250 },
                new int[] { 1, 200 },
                new int[] { 2, 200 },
                new int[] { 3, 200 },
                new int[] { 4, 150 },
                new int[] { 5, 200 },
                new int[] { 6, 200 },
                new int[] { 7, 200 },
                new int[] { 8, 150 },
                new int[] { 9, 200 },
                new int[] { 10, 200 },
                new int[] { 11, 150 }
        ));

        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean(billType.label() + "[" + billNo + "]" + timestamp + ".xlsx", stream);
    }

    @Override
    public VtuzxDownloadBean downloadStockTakeTaskDetail(VtuzxToken token, Map<String, Object> param) throws Exception{
        String billNo = VtuzxUtil.getString(param, "billNo");
        List<VtuzxMap> dataList = new ArrayList<>();
        Iterator<Document> taskDetailItr = stockTakeTaskDetailDao.findList(StockTakeTaskDetailDao.NAME + billNo, null, new Document("status", -1).append("TPALCount", 1).append("createTime", -1), 0, 0);

        while (taskDetailItr.hasNext()) {
            Document taskDetailDoc = taskDetailItr.next();
            String materialNo = VtuzxUtil.getString(taskDetailDoc,"materialNo");
            if (!"03010101042".equals(materialNo)){//不要沫沫的
                VtuzxMap taskDetails = new VtuzxMap(taskDetailDoc);
                taskDetails.remove(IVtuzxConst.Key_updateTime);
                taskDetails.remove(IVtuzxConst.Key_createTime);
                String id = VtuzxUtil.removeString(taskDetails, IVtuzxConst.Key__id);
                taskDetails.append("id", id);
                dataList.add(taskDetails);
            }
        }
        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());
        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "盘盈亏列表");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++, "物料编码"),
                new VtuzxExcelCell(col++, "物料名称"),
                new VtuzxExcelCell(col++, "物料规格"),
                new VtuzxExcelCell(col++, "单位"),
                new VtuzxExcelCell(col++, "批次"),
                new VtuzxExcelCell(col++, "账面库存"),
                new VtuzxExcelCell(col++, "账面箱数"),
                new VtuzxExcelCell(col++, "盘点库存"),
                new VtuzxExcelCell(col++, "盘点箱数"),
                new VtuzxExcelCell(col++, "复盘库存"),
                new VtuzxExcelCell(col++, "复盘箱数"),
                new VtuzxExcelCell(col++, "盘盈亏数"),
                new VtuzxExcelCell(col, "盘盈亏箱数")
        ));
        sheet0.addRow(header0);

        for(VtuzxMap detail: dataList) {
            col = 0;
            String materialNo = VtuzxUtil.getString(detail, "materialNo");
            String materialName = VtuzxUtil.getString(detail, "materialName");
            String materialSpec = VtuzxUtil.getString(detail, "materialSpec");
            String unitName = VtuzxUtil.getString(detail, "unitName");
            String batchNo = VtuzxUtil.getString(detail, "batchNo");
            Double bookCount = VtuzxUtil.getDouble(detail, "bookCount");
            Double bookBoxCount = VtuzxUtil.getDouble(detail, "bookBoxCount");
            Double count = VtuzxUtil.getDouble(detail, "count");
            Double boxCount = VtuzxUtil.getDouble(detail, "boxCount");
            Double reviewCount = VtuzxUtil.getDouble(detail, "reviewCount");
            Double reviewBoxCount = VtuzxUtil.getDouble(detail, "reviewBoxCount");
            Double TPALCount = VtuzxUtil.getDouble(detail, "TPALCount");
            Double TPALBoxCount = VtuzxUtil.getDouble(detail, "TPALBoxCount");

            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                    new VtuzxExcelCell(col++, materialNo),
                    new VtuzxExcelCell(col++, materialName),
                    new VtuzxExcelCell(col++, materialSpec),
                    new VtuzxExcelCell(col++, unitName),
                    new VtuzxExcelCell(col++, batchNo),
                    new VtuzxExcelCell(col++, bookCount),
                    new VtuzxExcelCell(col++, bookBoxCount),
                    new VtuzxExcelCell(col++, count),
                    new VtuzxExcelCell(col++, boxCount),
                    new VtuzxExcelCell(col++, reviewCount),
                    new VtuzxExcelCell(col++, reviewBoxCount),
                    new VtuzxExcelCell(col++, TPALCount),
                    new VtuzxExcelCell(col, TPALBoxCount)
            ));
            sheet0.addRow(dataRow);

        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[] { 0, 250 },
                new int[] { 1, 200 },
                new int[] { 2, 200 },
                new int[] { 3, 200 },
                new int[] { 4, 150 },
                new int[] { 5, 200 },
                new int[] { 6, 200 },
                new int[] { 7, 200 },
                new int[] { 8, 150 },
                new int[] { 9, 200 },
                new int[] { 10, 200 },
                new int[] { 11, 150 },
                new int[] { 12, 150 },
                new int[] { 13, 150 }
        ));

        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean( "[" + billNo + "]" + timestamp + ".xlsx", stream);
    }

    private VtuzxMap downloadProfitAndLossInner(String id) {
        Document profitAndLoss = profitAndLossDao.findById(new ObjectId(id));
        VtuzxMap profitAndLossMap = new VtuzxMap(profitAndLoss);
        String stockNo = VtuzxUtil.getString(profitAndLossMap, "stockNo");
        Document stock = stockDao.findOne(Criteria.where("fNumber").is(stockNo));
        String stockName = VtuzxUtil.getString(stock, "fName");
        profitAndLossMap.append("stockName", stockName);
        Iterator<Document> profitAndLossDetailItr = profitAndLossDetailDao.findList(Criteria.where("_pykdId").is(new ObjectId(id)), new Document("key", 1), 0, 0);
        VtuzxMap detailMap = FastUtil.itr2Map(profitAndLossDetailItr, "TPALkey");
        List<String> keyList = new ArrayList<>();
        List<Document> valList = new ArrayList<>();
        detailMap.forEach((key, val) -> {
            Document valMap = (Document) val;
            keyList.add(key);
            valList.add(valMap);
        });

        List<Document> boxList = new ArrayList<>();
        Iterator<Document> boxItr = profitAndLossDetailBoxDao.findList(Criteria.where("TPALkey").in(keyList).andOperator(Criteria.where("billNo").is(VtuzxUtil.getString(profitAndLoss,"billNo"))), new Document("TPALkey", 1), 0, 0);
        while (boxItr.hasNext()) {
            Document boxDoc = boxItr.next();
            boxList.add(boxDoc);
        }

        List<VtuzxMap> dataList = new ArrayList<>();
        for (Document val: valList) {
            String boxKey = VtuzxUtil.getString(val, "TPALkey");
            String locationNo = VtuzxUtil.getString(val, "locationNo");
            String materialNo = VtuzxUtil.getString(val, "materialNo");
            String materialSpec = VtuzxUtil.getString(val, "materialSpec");
            String materialName = VtuzxUtil.getString(val, "materialName");
            String unit = VtuzxUtil.getString(val, "unit");
            String unitName = VtuzxUtil.getString(val, "unitName");
            String batchNo = VtuzxUtil.getString(val, "batchNo");
            for (Document box: boxList) {
                VtuzxMap data = new VtuzxMap();
                String key = VtuzxUtil.getString(box, "TPALkey");
                String boxNo = VtuzxUtil.getString(box, "boxNo");
                double TPALCount = VtuzxUtil.getDouble(box, "TPALCount");
                if (key.equals(boxKey)) {
                    data.append("locationNo", locationNo);
                    data.append("materialSpec", materialSpec);
                    data.append("materialNo", materialNo);
                    data.append("materialName", materialName);
                    data.append("unit", unit);
                    data.append("unitName", unitName);
                    data.append("batchNo", batchNo);
                    data.append("boxNo", boxNo);
                    data.append("TPALCount", TPALCount);
                    data.append("key", key);
                    dataList.add(data);
                }
            }
        }

        return new VtuzxMap("header", profitAndLossMap).append("data", dataList);
    }

}
